package com.lkc.service.impl;

import java.util.Date;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;


import com.lkc.dao.SeckillDao;
import com.lkc.dao.SuccessKillDao;
import com.lkc.dto.Exposer;
import com.lkc.dto.SeckillExcution;
import com.lkc.entity.Seckill;
import com.lkc.entity.SuccessKilled;
import com.lkc.enums.SeckillStatEnum;
import com.lkc.exception.RepeatKillException;
import com.lkc.exception.SeckillCloseException;
import com.lkc.exception.SeckillException;
import com.lkc.service.SeckillService;

@Service
public class SeckillServiceImpl implements SeckillService{
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Autowired
	private SeckillDao seckillDao;
	@Autowired
	private SuccessKillDao successKillDao;
	
	//md5
	private final String slat = "dakwpodakwruq308dj0w9ue09dji";
	
	public List<Seckill> getSeckillList() {
		return seckillDao.queryAll(0, 4);
	}

	public Seckill getById(long seckilId) {
		return seckillDao.queryById(seckilId);
	}

	public Exposer exportSeckillUrl(long seckillId) {
		Seckill seckill =  seckillDao.queryById(seckillId);
		if(seckill == null){
			return new Exposer(false, seckillId);
		}
		Date startTime = seckill.getStartTime();
		Date endTime = seckill.getEndTime();
		Date nowTime = new Date();
		if(nowTime.getTime() > endTime.getTime() || nowTime.getTime() < startTime.getTime()){
			return new Exposer(false, seckillId, nowTime.getTime(), startTime.getTime(), endTime.getTime());
		}
		String md5 = getMD5(seckillId);;
		return new Exposer(true, md5, seckillId);
	}
	
	//制作md5
	private String getMD5(long seckillId){
		String base = seckillId + "/" + slat;
		String md5 = DigestUtils.md5DigestAsHex(base.getBytes());
		return md5;
	}
	
	@Transactional
	/**
	 * 使用注解控制事务的优点
	 * 1开发团队达成一致约定，明确标注事务方法的编程风格
	 * 2保证事务方法的实行时间尽可能段，不要穿插其他网络操作
	 * 3不是所有的方法都需要事务
	 */
	public SeckillExcution executeSeckill(long seckillId, long userPhone,
			String md5) throws SeckillException, RepeatKillException,
			SeckillCloseException {
		try {
			if(md5 == null || !md5.equals(getMD5(seckillId))){
				throw new SeckillException("seckill data rewrite");
			}
			
			//执行秒杀逻辑:减库存 + 记录购买行为
			Date killTime = new Date();
			int count = seckillDao.reduceNumber(seckillId, killTime);
			if(count <= 0){
				//没有跟新到操作
				throw new SeckillCloseException("seckill close");
			}else{
				//记录购买行为
				int insertNum = successKillDao.insertSuccessKilled(seckillId, userPhone);
				//唯一：seckillId, userPhone
				if(insertNum <= 0 ){
					throw new RepeatKillException("");
				}else{
					SuccessKilled successKilled =  successKillDao.queryByIdWithSeckill(seckillId, userPhone);
					return new SeckillExcution(seckillId, SeckillStatEnum.SUCCESS, successKilled);
					
				}
				
			}
		}catch(SeckillCloseException e1){
			throw e1;
		}catch(RepeatKillException e2){
			throw e2;
		}catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new SeckillException("seckill inner error:"+e.getMessage());
		}
	}

}
